package com.xb.loan.usercenter.account.impl;

import com.xb.loan.account.service.constant.AccountConst;
import com.xb.loan.account.service.exception.IAccountServiceException;
import com.xb.loan.log.exception.BizException;
import com.xb.loan.log.exception.IBizException;
import com.xb.loan.usercenter.account.dao.CashBillDao;
import com.xb.loan.usercenter.account.dao.CashInfoDao;
import com.xb.loan.usercenter.account.dao.CashStatDao;
import com.xb.loan.usercenter.account.dao.bean.CashBill;
import com.xb.loan.usercenter.account.dao.bean.CashInfo;
import com.xb.loan.usercenter.service.user.IUserService;
import com.xb.loan.usercenter.service.user.bean.UserInfoVO;
import com.xb.loan.usercenter.service.user.exception.IUserServiceException;
import com.xb.loan.util.cache.redis.comm.FormatOperation;
import com.xb.loan.util.log.DefaultLoggerFacotry;
import com.xb.loan.util.log.LogFormatUtil;
import com.xb.loan.util.mybatis.MyTransactional;
import com.xb.loan.util.strategy.TableNoAllocatorFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;
import java.util.Map;

public abstract class AbstractCashService {
	private static Logger logger = DefaultLoggerFacotry.getLogger(AbstractCashService.class);

	@Autowired
	protected CashInfoDao cashInfoDao;

	@Autowired
	protected CashBillDao cashBillDao;
	
	@Autowired
	protected CashStatDao cashStatDao;

	@Autowired(required = false)
	protected IUserService userInfoService;


	@MyTransactional
	public void addMoneyTransaction(UserInfoVO userInfo, CashInfo cashInfoDelta, CashBill cashBill) throws BizException {
		addMoneyAtomic(userInfo, cashInfoDelta, cashBill);
	}

	@MyTransactional
	public void deductMoneyTransaction(UserInfoVO userInfo, CashInfo cashInfoDelta, CashBill cashBill) throws BizException {
		deductMoneyAtomic(userInfo, cashInfoDelta, cashBill);
	}
	
	@MyTransactional
	public void deductCreditTransaction(UserInfoVO userInfo, CashInfo cashInfoDelta, CashBill cashBill) throws BizException {
		deductCreditAtomic(userInfo, cashInfoDelta, cashBill);
	}
	
	@MyTransactional
	public void deductTmpCreditTransaction(UserInfoVO userInfo, CashInfo cashInfoDelta, CashBill cashBill) throws BizException {
		deductTmpCreditAtomic(userInfo, cashInfoDelta, cashBill);
	}

	@MyTransactional
	public void transferTransaction(UserInfoVO outUserInfo, CashInfo outCashInfoDelta, CashBill outCashBill, UserInfoVO inUserInfo, CashInfo inCashInfoDelta, CashBill inCashBill) throws BizException {
		deductCreditAtomic(outUserInfo, outCashInfoDelta, outCashBill);
		addMoneyAtomic(inUserInfo, inCashInfoDelta, inCashBill);
	}
	
	@MyTransactional
	public void purchaseTransferTransaction(UserInfoVO outUserInfo, CashInfo outCashInfoDelta, CashBill outCashBill, UserInfoVO inUserInfo, CashInfo inCashInfoDelta, CashBill inCashBill) throws BizException {
		deductTmpCreditAtomic(outUserInfo, outCashInfoDelta, outCashBill);
		addMoneyAtomic(inUserInfo, inCashInfoDelta, inCashBill);
	}

	protected void addMoneyAtomic(UserInfoVO userInfo, CashInfo cashInfoDelta, CashBill cashBill) throws BizException {
		
		int length = cashInfoDao.addMoney(cashInfoDelta);
		if (length < 1)
			throw IBizException.systemException;
		
		// 获取最新的账户金额
        CashInfo cashInfo = cashInfoDao.queryCashInfoById(userInfo.getAccountId());
		
		// 设置账户金额
		cashBill.setLastMoney(cashInfo.getMoney() - cashInfoDelta.getMoney());
		// 设置账户余额
		cashBill.setCurMoney(cashInfo.getMoney());

		cashBillDao.insert(cashBill);
		
		statcash(userInfo.getAccountId(), cashBill.getBillType(), cashInfoDelta.getMoney());
	}

	protected void deductMoneyAtomic(UserInfoVO userInfo, CashInfo cashInfoDelta, CashBill cashBill) throws BizException {
		
		int length = cashInfoDao.deductMoney(cashInfoDelta);
		if (length < 1)
			throw IAccountServiceException.noEnoughMoney;
		
		// 获取最新的账户金额
        CashInfo cashInfo = cashInfoDao.queryCashInfoById(userInfo.getAccountId());
        
		// 设置账户金额
		cashBill.setLastMoney(cashInfo.getMoney() + cashInfoDelta.getMoney());
		// 设置账户余额
		cashBill.setCurMoney(cashInfo.getMoney());

		cashBillDao.insert(cashBill);
		
		statcash(userInfo.getAccountId(), cashBill.getBillType(), cashInfoDelta.getMoney());
	}
	
	protected void deductCreditAtomic(UserInfoVO userInfo, CashInfo cashInfoDelta, CashBill cashBill) throws BizException {
		
		int length = cashInfoDao.deductCredit(cashInfoDelta);
		if (length < 1)
			throw IAccountServiceException.noEnoughMoney;
		
		// 获取更新后的账户余额
        CashInfo cashInfo = cashInfoDao.queryCashInfoById(userInfo.getAccountId());
		
		// 设置账户金额
		cashBill.setLastMoney(cashInfo.getMoney() + cashInfoDelta.getMoney());
		// 设置账户余额
		cashBill.setCurMoney(cashInfo.getMoney());

		cashBillDao.insert(cashBill);
		
		statcash(userInfo.getAccountId(), cashBill.getBillType(), cashInfoDelta.getMoney());
	}
	
	protected void deductTmpCreditAtomic(UserInfoVO userInfo, CashInfo cashInfoDelta, CashBill cashBill) throws BizException {
	    
		int length = cashInfoDao.deductTmpCredit(cashInfoDelta);
		if (length < 1)
			throw IAccountServiceException.noEnoughMoney;
		
		// 获取更新后的账户余额
        CashInfo cashInfo = cashInfoDao.queryCashInfoById(userInfo.getAccountId());
        
		// 设置账户金额
		cashBill.setLastMoney(cashInfo.getMoney() + cashInfoDelta.getMoney());
		// 设置账户余额
		cashBill.setCurMoney(cashInfo.getMoney());

		cashBillDao.insert(cashBill);
		
		statcash(userInfo.getAccountId(), cashBill.getBillType(), cashInfoDelta.getMoney());
	}

	/**
	 * 现金账户累计
	 * @param accountId
	 * @param billType
	 * @param money
	 * 2015年12月25日 by wenju.song 
	 * @throws BizException 
	 */
	private void statcash(String accountId, int billType, double money) throws BizException {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("accountId", accountId);
		switch (billType) {
		case AccountConst.BillType_Purchase_Lottery:
			params.put("throw", money);
			break;
		case AccountConst.BillType_Purchase_Lottery_Refund:
			params.put("throw", -money);
			break;
		case AccountConst.BillType_Exchage_Prize:
			params.put("prize", money);
			params.put("smallPrize", money);
			break;
		case AccountConst.BillType_Marketing_Costs:
			params.put("commission", money);
			break;
		case AccountConst.BillType_Adjust_Marketing_Costs:
			params.put("adjust", money);
			break;
		case AccountConst.BillType_Proxy_Charge_In:
			params.put("transferCharge", money);
			break;
		case AccountConst.BillType_Proxy_Charge_Out:
			params.put("transferEncash", money);
			break;
		case AccountConst.BillType_Proxy_Encash_In:
			params.put("transferCharge", money);
			break;
		case AccountConst.BillType_Proxy_Encash_Out:
			params.put("transferEncash", money);
			break;
		case AccountConst.BillType_Proxy_Pay_Out:
			params.put("transferEncash", money);
			break;
		case AccountConst.BillType_Proxy_Pay_In:
			params.put("transferCharge", money);
			break;
		case AccountConst.BillType_Proxy_Pay_Refund_Out:
			params.put("transferCharge", -money);
			break;
		case AccountConst.BillType_Proxy_Pay_Refund_In:
			params.put("transferEncash", -money);
			break;
		case AccountConst.BillType_Proxy_Exchage_Out:
			params.put("transferEncash", money);
			break;
		case AccountConst.BillType_Proxy_Exchage_In:
			params.put("transferCharge", money);
			break;
		case AccountConst.BillType_Credit_Pay_Out:
			params.put("transferEncash", money);
			break;
		case AccountConst.BillType_Credit_Pay_In:
			params.put("transferCharge", money);
			break;
		case AccountConst.BillType_Credit_Pay_Refund_Out:
			params.put("transferCharge", -money);
			break;
		case AccountConst.BillType_Credit_Pay_Refund_In:
			params.put("transferEncash", -money);
			break;
		case AccountConst.BillType_Credit_Exchage_Encash:
			params.put("bankEncash", money);
			break;
		case AccountConst.BillType_Credit_Exchage_Refund:
			params.put("bankEncash", -money);
			break;
		case AccountConst.BillType_Charge:
			params.put("bankCharge", money);
			break;
		case AccountConst.BillType_OSS_Charge:
			params.put("bankCharge", money);
			break;
		case AccountConst.BillType_Encash:
			params.put("bankEncash", money);
			break;
		case AccountConst.BillType_Encash_Refund:
			params.put("bankEncash", -money);
			break;
		default:
			logger.warn(LogFormatUtil.getFormatMsg(new String[]{"module", "billType", "msg"},
				new Object[]{"account", billType, IAccountServiceException.invalidBillType.getMessage()}));
			throw IAccountServiceException.invalidBillType;
		}
		int size = cashStatDao.updateCashStat(params);
		if(size < 1){
			logger.warn(LogFormatUtil.getFormatMsg(new String[]{"module", "msg"},
				new Object[]{"account", IAccountServiceException.statCashException.getMessage()}));
			throw IAccountServiceException.statCashException;
		}
	}


	/**
	 * 根据userId查询用户信息，若返回null，抛用户不存在异常
	 * 
	 * @param userId
	 * @return
	 * @throws BizException
	 *                 2015年9月1日 by wenju.song
	 */
	protected UserInfoVO getUserInfo(String userId) throws BizException {
		UserInfoVO userInfo = userInfoService.queryUserInfoById(userId);
		if (userInfo == null) {
			logger.warn(IUserServiceException.userInfoMissing.getMessage(), IUserServiceException.userInfoMissing);
			throw IUserServiceException.userInfoMissing;
		}
		return userInfo;
	}

	/**
	 * 返回一个CashBill（记账明细）
	 * 
	 * @param userInfo
	 *                记账主体
	 * @param billType
	 *                记账类型
	 * @param foreignId
	 *                记账来源订单ID
	 * @return
	 * @throws BizException
	 *                 2015年9月2日 by wenju.song
	 */
	protected CashBill createCashBill(UserInfoVO userInfo, int billType, String foreignId, String accountPeriod) throws BizException {
		CashBill cashBill = new CashBill();
		cashBill.setBillId(FormatOperation.assemblySeq(AccountConst.PriKey_Name, AccountConst.PriKey_Length));
		cashBill.setAccountId(userInfo.getAccountId());
		cashBill.setUserId(userInfo.getUserId());
		cashBill.setPartnerId(userInfo.getPartnerId());
		cashBill.setFeeType(AccountConst.FeeType_Default);
		cashBill.setBillType(billType);
		cashBill.setAccountPeriod(accountPeriod);
		cashBill.setForeignId(foreignId);
		cashBill.setTableId(TableNoAllocatorFactory.getAllocator("t_cash_bill").getTableNo(userInfo.getUserId()));
		return cashBill;
	}
}
